//SPDX-FileCopyrightText: Copyright 2022-2024 深圳市同心圆网络有限公司
//SPDX-License-Identifier: GPL-3.0-only

package issue_dao

import (
	"context"
	"regexp"
	"time"

	"atomgit.com/openlinksaas-org/api-server/dao/db_ns"
	"atomgit.com/openlinksaas-org/proto-gen-go.git/project_issue_api"
	"go.mongodb.org/mongo-driver/bson"
	"go.mongodb.org/mongo-driver/bson/primitive"
	"go.mongodb.org/mongo-driver/mongo"
	"go.mongodb.org/mongo-driver/mongo/options"
)

type BasicIssueInfoDbItem struct {
	Title     string   `bson:"title"`
	Content   string   `bson:"content"`
	TagIdList []string `bson:"tagIdList"`
}

type ExtraTaskInfoDbItem struct {
	Priority int32 `bson:"priority"`
}

type ExtraBugInfoDbItem struct {
	SoftwareVersion string `bson:"softwareVersion"`
	Level           int32  `bson:"level"`
	Priority        int32  `bson:"priority"`
}
type IssueInfoDbItem struct {
	IssueId            string               `bson:"_id"`
	IssueType          int32                `bson:"issueType"`
	IssueIndex         int64                `bson:"issueIndex"`
	BasicInfo          BasicIssueInfoDbItem `bson:"basicInfo"`
	ProjectId          string               `bson:"projectId"`
	CreateTime         int64                `bson:"createTime"`
	UpdateTime         int64                `bson:"updateTime"`
	State              int32                `bson:"state"`
	ProcessStage       int32                `bson:"processStage"`
	CreateUserId       string               `bson:"createUserId"`
	ExecUserId         string               `bson:"execUserId"`
	CheckUserId        string               `bson:"checkUserId"`
	MsgCount           uint32               `bson:"msgCount"`
	SpritId            string               `bson:"spritId"`
	SpritTime          int64                `bson:"spritTime"`
	HasStartTime       bool                 `bson:"hasStartTime"`
	StartTime          int64                `bson:"startTime"`
	HasEndTime         bool                 `bson:"hasEndTime"`
	EndTime            int64                `bson:"endTime"`
	HasEstimateMinutes bool                 `bson:"hasEstimateMinutes"`
	EstimateMinutes    uint32               `bson:"estimateMinutes"`
	HasRemainMinutes   bool                 `bson:"hasRemainMinutes"`
	RemainMinutes      uint32               `bson:"remainMinutes"`
	DependMeCount      uint32               `bson:"dependMeCount"`
	ReOpenCount        uint32               `bson:"reOpenCount"`
	HasDeadLineTime    bool                 `bson:"hasDeadLineTime"`
	DeadLineTime       int64                `bson:"deadLineTime"`
	ExtraTaskInfo      *ExtraTaskInfoDbItem `bson:"extraTaskInfo"`
	ExtraBugInfo       *ExtraBugInfoDbItem  `bson:"extraBugInfo"`
}

func (item *IssueInfoDbItem) ToIssueInfo(spritName string, userPerm *project_issue_api.UserIssuePerm, subIssueStatus *project_issue_api.SubIssueStatus) *project_issue_api.IssueInfo {
	tagIdList := []string{}
	if item.BasicInfo.TagIdList != nil {
		tagIdList = item.BasicInfo.TagIdList
	}
	ret := &project_issue_api.IssueInfo{
		IssueId:    item.IssueId,
		IssueType:  project_issue_api.ISSUE_TYPE(item.IssueType),
		IssueIndex: item.IssueIndex,
		BasicInfo: &project_issue_api.BasicIssueInfo{
			Title:     item.BasicInfo.Title,
			Content:   item.BasicInfo.Content,
			TagIdList: tagIdList,
		},
		ProjectId:          item.ProjectId,
		CreateTime:         item.CreateTime,
		UpdateTime:         item.UpdateTime,
		State:              project_issue_api.ISSUE_STATE(item.State),
		ProcessStage:       project_issue_api.PROCESS_STAGE(item.ProcessStage),
		CreateUserId:       item.CreateUserId,
		ExecUserId:         item.ExecUserId,
		CheckUserId:        item.CheckUserId,
		MsgCount:           item.MsgCount,
		SpritId:            item.SpritId,
		SpritName:          spritName,
		HasStartTime:       item.HasStartTime,
		StartTime:          item.StartTime,
		HasEndTime:         item.HasEndTime,
		EndTime:            item.EndTime,
		HasEstimateMinutes: item.HasEstimateMinutes,
		EstimateMinutes:    item.EstimateMinutes,
		HasRemainMinutes:   item.HasRemainMinutes,
		RemainMinutes:      item.RemainMinutes,
		UserIssuePerm:      userPerm,
		HasDeadLineTime:    item.HasDeadLineTime,
		DeadLineTime:       item.DeadLineTime,
		DependMeCount:      item.DependMeCount,
		ReOpenCount:        item.ReOpenCount,
		TagInfoList:        []*project_issue_api.IssueTag{},
		SubIssueStatus:     subIssueStatus,
		WatchUserList:      []*project_issue_api.WatchUser{},
	}
	if item.IssueType == int32(project_issue_api.ISSUE_TYPE_ISSUE_TYPE_TASK) && item.ExtraTaskInfo != nil {
		ret.ExtraInfo = &project_issue_api.IssueInfo_ExtraTaskInfo{
			ExtraTaskInfo: &project_issue_api.ExtraTaskInfo{
				Priority: project_issue_api.TASK_PRIORITY(item.ExtraTaskInfo.Priority),
			},
		}
	} else if item.IssueType == int32(project_issue_api.ISSUE_TYPE_ISSUE_TYPE_BUG) && item.ExtraBugInfo != nil {
		ret.ExtraInfo = &project_issue_api.IssueInfo_ExtraBugInfo{
			ExtraBugInfo: &project_issue_api.ExtraBugInfo{
				SoftwareVersion: item.ExtraBugInfo.SoftwareVersion,
				Level:           project_issue_api.BUG_LEVEL(item.ExtraBugInfo.Level),
				Priority:        project_issue_api.BUG_PRIORITY(item.ExtraBugInfo.Priority),
			},
		}
	}
	return ret
}

type _IssueDao struct {
	mc *mongo.Collection
}

func newIssueDao(db *mongo.Database) (*_IssueDao, error) {
	mc := db.Collection(db_ns.DB_NS_ISSUE + "info")
	_, err := mc.Indexes().CreateMany(context.Background(), []mongo.IndexModel{
		{
			Keys: bson.D{{"projectId", 1}, {"updateTime", 1}},
		},
		{
			Keys: bson.D{{"projectId", 1}, {"spritId", 1}},
		},
		{
			Keys: bson.D{{"projectId", 1}, {"createUserId", 1}, {"issueType", 1}, {"state", 1}},
		},
		{
			Keys: bson.D{{"projectId", 1}, {"execUserId", 1}, {"issueType", 1}, {"state", 1}},
		},
		{
			Keys: bson.D{{"projectId", 1}, {"checkUserId", 1}, {"issueType", 1}, {"state", 1}},
		},
	})
	if err != nil {
		return nil, err
	}
	return &_IssueDao{mc: mc}, nil
}

func (db *_IssueDao) Insert(ctx context.Context, dbItem *IssueInfoDbItem) error {
	_, err := db.mc.InsertOne(ctx, dbItem)
	return err
}

func (db *_IssueDao) Remove(ctx context.Context, projectId, issueId string) error {
	_, err := db.mc.DeleteOne(ctx, bson.M{"_id": issueId, "projectId": projectId})
	return err
}

func (db *_IssueDao) Get(ctx context.Context, projectId, issueId string) (*IssueInfoDbItem, error) {
	retItem := &IssueInfoDbItem{}
	err := db.mc.FindOne(ctx, bson.M{"_id": issueId, "projectId": projectId}).Decode(retItem)
	return retItem, err
}

func (db *_IssueDao) Exist(ctx context.Context, projectId, issueId string) (bool, error) {
	count, err := db.mc.CountDocuments(ctx, bson.M{"_id": issueId, "projectId": projectId})
	return count > 0, err
}

func (db *_IssueDao) Update(ctx context.Context, projectId, issueId string,
	basicItem *BasicIssueInfoDbItem, extraTaskItem *ExtraTaskInfoDbItem, extraBugItem *ExtraBugInfoDbItem) error {
	setItem := bson.M{"basicInfo": basicItem}
	if extraTaskItem != nil {
		setItem["extraTaskInfo"] = extraTaskItem
	}
	if extraBugItem != nil {
		setItem["extraBugInfo"] = extraBugItem
	}
	_, err := db.mc.UpdateOne(ctx, bson.M{"_id": issueId, "projectId": projectId}, bson.M{"$set": setItem})
	return err
}

func (db *_IssueDao) UpdateTitle(ctx context.Context, projectId, issueId, title string) error {
	_, err := db.mc.UpdateOne(ctx, bson.M{"_id": issueId, "projectId": projectId}, bson.M{"$set": bson.M{"basicInfo.title": title}})
	return err
}

func (db *_IssueDao) UpdateTagIdList(ctx context.Context, projectId, issueId string, tagIdList []string) error {
	_, err := db.mc.UpdateOne(ctx, bson.M{"_id": issueId, "projectId": projectId}, bson.M{"$set": bson.M{"basicInfo.tagIdList": tagIdList}})
	return err
}

func (db *_IssueDao) UpdateContent(ctx context.Context, projectId, issueId, content string) error {
	_, err := db.mc.UpdateOne(ctx, bson.M{"_id": issueId, "projectId": projectId}, bson.M{"$set": bson.M{"basicInfo.content": content}})
	return err
}

func (db *_IssueDao) UpdateExtraInfo(ctx context.Context, projectId, issueId string,
	extraTaskItem *ExtraTaskInfoDbItem, extraBugItem *ExtraBugInfoDbItem) error {
	setItem := bson.M{}
	if extraTaskItem != nil {
		setItem["extraTaskInfo"] = extraTaskItem
	}
	if extraBugItem != nil {
		setItem["extraBugInfo"] = extraBugItem
	}
	_, err := db.mc.UpdateOne(ctx, bson.M{"_id": issueId, "projectId": projectId}, bson.M{"$set": setItem})
	return err
}

func (db *_IssueDao) AssignExecUser(ctx context.Context, projectId, issueId, execUserId string, newStat, processStage int32, nowTime int64) error {
	_, err := db.mc.UpdateOne(ctx, bson.M{"_id": issueId, "projectId": projectId},
		bson.M{"$set": bson.M{"execUserId": execUserId, "state": newStat, "processStage": processStage, "updateTime": nowTime}})
	return err
}
func (db *_IssueDao) AssignCheckUser(ctx context.Context, projectId, issueId, checkUserId string, nowTime int64) error {
	_, err := db.mc.UpdateOne(ctx, bson.M{"_id": issueId, "projectId": projectId},
		bson.M{"$set": bson.M{"checkUserId": checkUserId, "updateTime": nowTime}})
	return err
}

func (db *_IssueDao) UpdateState(ctx context.Context, projectId, issueId string, newStat, processStage int32, nowTime int64) error {
	_, err := db.mc.UpdateOne(ctx, bson.M{"_id": issueId, "projectId": projectId},
		bson.M{"$set": bson.M{"state": newStat, "processStage": processStage, "updateTime": nowTime}})
	return err
}

func (db *_IssueDao) Count(ctx context.Context, projectId string, listParam *project_issue_api.ListParam, watchIssueIdList []string) (uint32, error) {
	query := db.genListQuery(listParam, watchIssueIdList)
	query["projectId"] = projectId
	count, err := db.mc.CountDocuments(ctx, query)
	return uint32(count), err
}

func (db *_IssueDao) List(ctx context.Context, projectId string, listParam *project_issue_api.ListParam,
	watchIssueIdList []string,
	sortKey project_issue_api.SORT_KEY, sortType project_issue_api.SORT_TYPE,
	offset, limit int64) ([]*IssueInfoDbItem, error) {
	query := db.genListQuery(listParam, watchIssueIdList)
	query["projectId"] = projectId
	sortParam := db.genSortItem(sortKey, sortType)
	cursor, err := db.mc.Find(ctx, query, options.Find().SetSort(sortParam).SetSkip(offset).SetLimit(limit))
	if err != nil {
		return nil, err
	}
	retList := []*IssueInfoDbItem{}
	err = cursor.All(ctx, &retList)
	return retList, err
}

// 列出未关闭的，并且dependCount>0的issue
func (db *_IssueDao) ListByDependMe(ctx context.Context, projectId string) ([]*IssueInfoDbItem, error) {
	cursor, err := db.mc.Find(ctx, bson.M{
		"projectId":     projectId,
		"dependMeCount": bson.M{"$gt": 0},
		"state":         bson.M{"$ne": uint32(project_issue_api.ISSUE_STATE_ISSUE_STATE_CLOSE)},
	})
	if err != nil {
		return nil, err
	}
	retList := []*IssueInfoDbItem{}
	err = cursor.All(ctx, &retList)
	return retList, err
}

// 列出未关闭的, 并且设置了deadLineTime的issue
func (db *_IssueDao) ListByDeadLineTime(ctx context.Context, projectId string) ([]*IssueInfoDbItem, error) {
	cursor, err := db.mc.Find(ctx, bson.M{
		"projectId":       projectId,
		"hasDeadLineTime": true,
		"state":           bson.M{"$ne": uint32(project_issue_api.ISSUE_STATE_ISSUE_STATE_CLOSE)},
	})
	if err != nil {
		return nil, err
	}
	retList := []*IssueInfoDbItem{}
	err = cursor.All(ctx, &retList)
	return retList, err
}

// 列出未关闭的，并且reopenCount>0的issue
func (db *_IssueDao) ListByReOpen(ctx context.Context, projectId string) ([]*IssueInfoDbItem, error) {
	cursor, err := db.mc.Find(ctx, bson.M{
		"projectId":   projectId,
		"reOpenCount": bson.M{"$gt": 0},
		"state":       bson.M{"$ne": uint32(project_issue_api.ISSUE_STATE_ISSUE_STATE_CLOSE)},
	})
	if err != nil {
		return nil, err
	}
	retList := []*IssueInfoDbItem{}
	err = cursor.All(ctx, &retList)
	return retList, err
}

type IssueIdDbItem struct {
	IssueId string `bson:"_id"`
}

func (db *_IssueDao) ListId(ctx context.Context, projectId string, listParam *project_issue_api.ListParam,
	watchIssueIdList []string,
	sortKey project_issue_api.SORT_KEY, sortType project_issue_api.SORT_TYPE, maxCount int64) ([]string, error) {
	if maxCount == 0 {
		return []string{}, nil
	}
	query := db.genListQuery(listParam, watchIssueIdList)
	query["projectId"] = projectId
	sortParam := db.genSortItem(sortKey, sortType)
	cursor, err := db.mc.Find(ctx, query, options.Find().SetSort(sortParam).SetLimit(maxCount).SetProjection(bson.M{"_id": 1}))
	if err != nil {
		return nil, err
	}
	tmpList := []*IssueIdDbItem{}
	err = cursor.All(ctx, &tmpList)
	if err != nil {
		return nil, err
	}
	retList := []string{}
	for _, tmpItem := range tmpList {
		retList = append(retList, tmpItem.IssueId)
	}
	return retList, nil
}

func (db *_IssueDao) ListById(ctx context.Context, projectId string, issueIdList []string) ([]*IssueInfoDbItem, error) {
	if len(issueIdList) == 0 {
		return []*IssueInfoDbItem{}, nil
	}
	cursor, err := db.mc.Find(ctx, bson.M{"_id": bson.M{"$in": issueIdList}, "projectId": projectId})
	if err != nil {
		return nil, err
	}
	retList := []*IssueInfoDbItem{}
	err = cursor.All(ctx, &retList)
	return retList, err
}

func (db *_IssueDao) ListByIdOnly(ctx context.Context, issueIdList []string) ([]*IssueInfoDbItem, error) {
	if len(issueIdList) == 0 {
		return []*IssueInfoDbItem{}, nil
	}
	cursor, err := db.mc.Find(ctx, bson.M{"_id": bson.M{"$in": issueIdList}})
	if err != nil {
		return nil, err
	}
	retList := []*IssueInfoDbItem{}
	err = cursor.All(ctx, &retList)
	return retList, err
}

func (db *_IssueDao) CountMyTodo(ctx context.Context, projectIdList []string, userId string) (uint32, error) {
	query := db.genMyTodoQuery(projectIdList, userId)
	count, err := db.mc.CountDocuments(ctx, query)
	return uint32(count), err
}

func (db *_IssueDao) ListMyTodo(ctx context.Context, projectIdList []string, userId string, sortKey project_issue_api.SORT_KEY, sortType project_issue_api.SORT_TYPE,
	offset, limit int64) ([]*IssueInfoDbItem, error) {
	query := db.genMyTodoQuery(projectIdList, userId)
	sortParam := db.genSortItem(sortKey, sortType)
	cursor, err := db.mc.Find(ctx, query, options.Find().SetSort(sortParam).SetSkip(offset).SetLimit(limit))
	if err != nil {
		return nil, err
	}
	retList := []*IssueInfoDbItem{}
	err = cursor.All(ctx, &retList)
	return retList, err
}

func (db *_IssueDao) genMyTodoQuery(projectIdList []string, userId string) bson.M {
	condList := []bson.M{}
	condList = append(condList, bson.M{"$or": []bson.M{
		{"execUserId": userId},
		{"checkUserId": userId},
	}})
	condList = append(condList, bson.M{"projectId": bson.M{"$in": projectIdList}})
	condList = append(condList, bson.M{"state": bson.M{"$ne": int32(project_issue_api.ISSUE_STATE_ISSUE_STATE_CLOSE)}})
	return bson.M{"$and": condList}
}

func (db *_IssueDao) genSortItem(sortKey project_issue_api.SORT_KEY, sortType project_issue_api.SORT_TYPE) bson.D {
	sortValue := 1
	if sortType == project_issue_api.SORT_TYPE_SORT_TYPE_DSC {
		sortValue = -1
	}
	if sortKey == project_issue_api.SORT_KEY_SORT_KEY_UPDATE_TIME {
		return bson.D{{"updateTime", sortValue}}
	} else if sortKey == project_issue_api.SORT_KEY_SORT_KEY_CREATE_TIME {
		return bson.D{{"createTime", sortValue}}
	} else if sortKey == project_issue_api.SORT_KEY_SORT_KEY_STATE {
		return bson.D{{"state", sortValue}}
	} else if sortKey == project_issue_api.SORT_KEY_SORT_KEY_SPRIT {
		return bson.D{{"spritTime", sortValue}, {"updateTime", sortValue}}
	} else if sortKey == project_issue_api.SORT_KEY_SORT_KEY_START_TIME {
		return bson.D{{"startTime", sortValue}, {"updateTime", sortValue}}
	} else if sortKey == project_issue_api.SORT_KEY_SORT_KEY_END_TIME {
		return bson.D{{"endTime", sortValue}, {"updateTime", sortValue}}
	} else if sortKey == project_issue_api.SORT_KEY_SORT_KEY_ESTIMATE_MINUTES {
		return bson.D{{"estimateMinutes", sortValue}, {"updateTime", sortValue}}
	} else if sortKey == project_issue_api.SORT_KEY_SORT_KEY_REMAIN_MINUTES {
		return bson.D{{"remainMinutes", sortValue}, {"updateTime", sortValue}}
	} else if sortKey == project_issue_api.SORT_KEY_SORT_KEY_TASK_PRIORITY {
		return bson.D{{"extraTaskInfo.priority", sortValue}, {"updateTime", sortValue}}
	} else if sortKey == project_issue_api.SORT_KEY_SORT_KEY_SOFTWARE_VERSION {
		return bson.D{{"extraBugInfo.softwareVersion", sortValue}, {"updateTime", sortValue}}
	} else if sortKey == project_issue_api.SORT_KEY_SORT_KEY_BUG_LEVEL {
		return bson.D{{"extraBugInfo.level", sortValue}, {"updateTime", sortValue}}
	} else if sortKey == project_issue_api.SORT_KEY_SORT_KEY_BUG_PRIORITY {
		return bson.D{{"extraBugInfo.priority", sortValue}, {"updateTime", sortValue}}
	}
	return bson.D{{"updateTime", sortValue}}
}

func (db *_IssueDao) genListQuery(listParam *project_issue_api.ListParam, watchIssueIdList []string) bson.M {
	condList := []bson.M{}
	if listParam.FilterByIssueType {
		condList = append(condList, bson.M{"issueType": int32(listParam.IssueType)})
	}
	if listParam.FilterByState {
		tmpList := []int32{}
		for _, state := range listParam.StateList {
			tmpList = append(tmpList, int32(state))
		}
		condList = append(condList, bson.M{"state": bson.M{"$in": tmpList}})
	}

	if listParam.FilterByCreateUserId {
		condList = append(condList, bson.M{"createUserId": bson.M{"$in": listParam.CreateUserIdList}})
	}
	if listParam.FilterByAssginUserId {
		if listParam.AssginUserType == project_issue_api.ASSGIN_USER_TYPE_ASSGIN_USER_ALL {
			condList = append(condList, bson.M{"$or": []bson.M{
				{"execUserId": bson.M{"$in": listParam.AssginUserIdList}},
				{"checkUserId": bson.M{"$in": listParam.AssginUserIdList}},
			}})
		} else if listParam.AssginUserType == project_issue_api.ASSGIN_USER_TYPE_ASSGIN_USER_EXEC {
			condList = append(condList, bson.M{"execUserId": bson.M{"$in": listParam.AssginUserIdList}})
		} else if listParam.AssginUserType == project_issue_api.ASSGIN_USER_TYPE_ASSGIN_USER_CHECK {
			condList = append(condList, bson.M{"checkUserId": bson.M{"$in": listParam.AssginUserIdList}})
		}
	}
	if listParam.FilterBySpritId {
		condList = append(condList, bson.M{"spritId": bson.M{"$in": listParam.SpritIdList}})
	}
	if listParam.FilterByCreateTime {
		condList = append(condList, bson.M{"createTime": bson.M{"$gte": listParam.FromCreateTime, "$lte": listParam.ToCreateTime}})
	}
	if listParam.FilterByUpdateTime {
		condList = append(condList, bson.M{"updateTime": bson.M{"$gte": listParam.FromUpdateTime, "$lte": listParam.ToUpdateTime}})
	}

	if listParam.FilterByTaskPriority {
		tmpList := []int32{}
		for _, priority := range listParam.TaskPriorityList {
			tmpList = append(tmpList, int32(priority))
		}
		condList = append(condList, bson.M{"extraTaskInfo.priority": bson.M{"$in": tmpList}})
	}
	if listParam.FilterBySoftwareVersion {
		condList = append(condList, bson.M{"extraBugInfo.softwareVersion": bson.M{"$in": listParam.SoftwareVersionList}})
	}
	if listParam.FilterByBugLevel {
		tmpList := []int32{}
		for _, priority := range listParam.TaskPriorityList {
			tmpList = append(tmpList, int32(priority))
		}
		condList = append(condList, bson.M{"extraBugInfo.level": bson.M{"$in": tmpList}})
	}
	if listParam.FilterByBugPriority {
		tmpList := []int32{}
		for _, priority := range listParam.TaskPriorityList {
			tmpList = append(tmpList, int32(priority))
		}
		condList = append(condList, bson.M{"extraBugInfo.priority": bson.M{"$in": tmpList}})
	}
	if listParam.FilterByTitleKeyword {
		condList = append(condList, bson.M{"basicInfo.title": primitive.Regex{
			Pattern: regexp.QuoteMeta(listParam.TitleKeyword),
			Options: "i",
		}})
	}
	if listParam.FilterByTagIdList {
		condList = append(condList, bson.M{"basicInfo.tagIdList": bson.M{"$in": listParam.TagIdList}})
	}
	if listParam.FilterByWatch {
		condList = append(condList, bson.M{"_id": bson.M{"$in": watchIssueIdList}})
	}
	if len(condList) > 0 {
		return bson.M{"$and": condList}
	} else {
		return bson.M{}
	}

}

func (db *_IssueDao) Distinct(ctx context.Context, projectId string, attrKey project_issue_api.ATTR_KEY) ([]string, error) {
	fieldName := "spritId"
	if attrKey == project_issue_api.ATTR_KEY_ATTR_KEY_SOFTWARE_VERSION {
		fieldName = "extraBugInfo.softwareVersion"
	}
	tmpList, err := db.mc.Distinct(ctx, fieldName, bson.M{"projectId": projectId})
	if err != nil {
		return nil, err
	}
	retList := []string{}
	for _, tmpValue := range tmpList {
		if tmpValue.(string) != "" {
			retList = append(retList, tmpValue.(string))
		}
	}
	return retList, nil
}

func (db *_IssueDao) DistinctIssueIdByProject(ctx context.Context, projectId string) ([]string, error) {
	tmpList, err := db.mc.Distinct(ctx, "_id", bson.M{"projectId": projectId})
	if err != nil {
		return nil, err
	}
	retList := []string{}
	for _, tmpValue := range tmpList {
		if tmpValue.(string) != "" {
			retList = append(retList, tmpValue.(string))
		}
	}
	return retList, nil
}

func (db *_IssueDao) SetSprit(ctx context.Context, projectId, issueId, spritId string, spritTime int64, nowTime int64) error {
	_, err := db.mc.UpdateOne(ctx, bson.M{"_id": issueId, "projectId": projectId},
		bson.M{"$set": bson.M{"spritId": spritId, "spritTime": spritTime, "updateTime": nowTime}})
	return err
}

func (db *_IssueDao) SetStartTime(ctx context.Context, projectId, issueId string, startTime int64, nowTime int64) error {
	_, err := db.mc.UpdateOne(ctx, bson.M{"_id": issueId, "projectId": projectId},
		bson.M{"$set": bson.M{"hasStartTime": true, "startTime": startTime, "updateTime": nowTime}})
	return err
}

func (db *_IssueDao) UnsetStartTime(ctx context.Context, projectId, issueId string, nowTime int64) error {
	_, err := db.mc.UpdateOne(ctx, bson.M{"_id": issueId, "projectId": projectId},
		bson.M{"$set": bson.M{"hasStartTime": false, "startTime": 0, "updateTime": nowTime}})
	return err
}

func (db *_IssueDao) SetEndTime(ctx context.Context, projectId, issueId string, endTime int64, nowTime int64) error {
	_, err := db.mc.UpdateOne(ctx, bson.M{"_id": issueId, "projectId": projectId},
		bson.M{"$set": bson.M{"hasEndTime": true, "endTime": endTime, "updateTime": nowTime}})
	return err
}

func (db *_IssueDao) UnsetEndTime(ctx context.Context, projectId, issueId string, nowTime int64) error {
	_, err := db.mc.UpdateOne(ctx, bson.M{"_id": issueId, "projectId": projectId},
		bson.M{"$set": bson.M{"hasEndTime": false, "endTime": 0, "updateTime": nowTime}})
	return err
}

func (db *_IssueDao) SetEstimateMinutes(ctx context.Context, projectId, issueId string, estimateMinutes int32, nowTime int64) error {
	_, err := db.mc.UpdateOne(ctx, bson.M{"_id": issueId, "projectId": projectId},
		bson.M{"$set": bson.M{"hasEstimateMinutes": true, "estimateMinutes": estimateMinutes, "updateTime": nowTime}})
	return err
}

func (db *_IssueDao) UnsetEstimateMinutes(ctx context.Context, projectId, issueId string, nowTime int64) error {
	_, err := db.mc.UpdateOne(ctx, bson.M{"_id": issueId, "projectId": projectId},
		bson.M{"$set": bson.M{"hasEstimateMinutes": false, "estimateMinutes": 0, "updateTime": nowTime}})
	return err
}

func (db *_IssueDao) SetRemainMinutes(ctx context.Context, projectId, issueId string, remainMinutes int32, nowTime int64) error {
	_, err := db.mc.UpdateOne(ctx, bson.M{"_id": issueId, "projectId": projectId},
		bson.M{"$set": bson.M{"hasRemainMinutes": true, "remainMinutes": remainMinutes, "updateTime": nowTime}})
	return err
}

func (db *_IssueDao) UnsetRemainMinutes(ctx context.Context, projectId, issueId string, nowTime int64) error {
	_, err := db.mc.UpdateOne(ctx, bson.M{"_id": issueId, "projectId": projectId},
		bson.M{"$set": bson.M{"hasRemainMinutes": false, "remainMinutes": 0, "updateTime": nowTime}})
	return err
}

func (db *_IssueDao) CalcCreateCount(ctx context.Context, projectId, userId string, issueType project_issue_api.ISSUE_TYPE) (uint32, error) {
	count, err := db.mc.CountDocuments(ctx, bson.M{"projectId": projectId, "issueType": int32(issueType), "createUserId": userId})
	return uint32(count), err
}

func (db *_IssueDao) CalcUnExecCount(ctx context.Context, projectId, userId string, issueType project_issue_api.ISSUE_TYPE) (uint32, error) {
	count, err := db.mc.CountDocuments(ctx, bson.M{"projectId": projectId, "issueType": int32(issueType), "execUserId": userId,
		"state": bson.M{"$ne": int32(project_issue_api.ISSUE_STATE_ISSUE_STATE_CLOSE)}})
	return uint32(count), err
}

func (db *_IssueDao) CalcUnCheckCount(ctx context.Context, projectId, userId string, issueType project_issue_api.ISSUE_TYPE) (uint32, error) {
	count, err := db.mc.CountDocuments(ctx, bson.M{"projectId": projectId, "issueType": int32(issueType), "checkUserId": userId,
		"state": bson.M{"$ne": int32(project_issue_api.ISSUE_STATE_ISSUE_STATE_CLOSE)}})
	return uint32(count), err
}

func (db *_IssueDao) CalcExecDoneCount(ctx context.Context, projectId, userId string, issueType project_issue_api.ISSUE_TYPE) (uint32, error) {
	count, err := db.mc.CountDocuments(ctx, bson.M{"projectId": projectId, "issueType": int32(issueType), "execUserId": userId,
		"state": int32(project_issue_api.ISSUE_STATE_ISSUE_STATE_CLOSE)})
	return uint32(count), err
}

func (db *_IssueDao) CalcCheckDoneCount(ctx context.Context, projectId, userId string, issueType project_issue_api.ISSUE_TYPE) (uint32, error) {
	count, err := db.mc.CountDocuments(ctx, bson.M{"projectId": projectId, "issueType": int32(issueType), "checkUserId": userId,
		"state": int32(project_issue_api.ISSUE_STATE_ISSUE_STATE_CLOSE)})
	return uint32(count), err
}

func (db *_IssueDao) IncMsgCount(ctx context.Context, projectId, issueId string) error {
	_, err := db.mc.UpdateOne(ctx, bson.M{"_id": issueId, "projectId": projectId}, bson.M{"$inc": bson.M{"msgCount": 1}})
	return err
}

func (db *_IssueDao) DecMsgCount(ctx context.Context, projectId, issueId string) error {
	_, err := db.mc.UpdateOne(ctx, bson.M{"_id": issueId, "projectId": projectId}, bson.M{"$inc": bson.M{"msgCount": -1}})
	return err
}

func (db *_IssueDao) ListByProjectId(ctx context.Context, projectId string) ([]*IssueInfoDbItem, error) {
	cursor, err := db.mc.Find(ctx, bson.M{"projectId": projectId})
	if err != nil {
		return nil, err
	}
	retList := []*IssueInfoDbItem{}
	err = cursor.All(ctx, &retList)
	return retList, err
}

func (db *_IssueDao) ClearSprit(ctx context.Context, projectId string, spritId string) error {
	nowTime := time.Now().UnixNano() / 1e6
	_, err := db.mc.UpdateMany(ctx, bson.M{"projectId": projectId, "spritId": spritId},
		bson.M{"$set": bson.M{"spritId": "", "updateTime": nowTime}})
	return err
}

func (db *_IssueDao) FixMissFieldData(ctx context.Context) error {
	_, err := db.mc.UpdateMany(ctx, bson.M{}, bson.M{"$set": bson.M{
		"dependMeCount":   0,
		"reOpenCount":     0,
		"hasDeadLineTime": false,
		"deadLineTime":    0,
	}})
	return err
}

func (db *_IssueDao) UpdateDependMeCount(ctx context.Context, projectId, issueId string, count int) error {
	_, err := db.mc.UpdateOne(ctx, bson.M{"_id": issueId, "projectId": projectId},
		bson.M{"$set": bson.M{"dependMeCount": count}})
	return err
}

func (db *_IssueDao) IncReOpenCount(ctx context.Context, projectId, issueId string) error {
	_, err := db.mc.UpdateOne(ctx, bson.M{"_id": issueId, "projectId": projectId}, bson.M{"$inc": bson.M{"reOpenCount": 1}})
	return err
}

func (db *_IssueDao) SetDeadLineTime(ctx context.Context, projectId, issueId string, deadLineTime, nowTime int64) error {
	_, err := db.mc.UpdateOne(ctx, bson.M{"_id": issueId, "projectId": projectId},
		bson.M{"$set": bson.M{"hasDeadLineTime": true, "deadLineTime": deadLineTime, "updateTime": nowTime}})
	return err
}

func (db *_IssueDao) UnsetDeadLineTime(ctx context.Context, projectId, issueId string, nowTime int64) error {
	_, err := db.mc.UpdateOne(ctx, bson.M{"_id": issueId, "projectId": projectId},
		bson.M{"$set": bson.M{"hasDeadLineTime": false, "deadLineTime": 0, "updateTime": nowTime}})
	return err
}
